package com.yunji.cabinet.device;

import com.yunji.common.exception.device.DeviceErrorEnum;
import com.yunji.common.exception.device.DeviceException;
import com.yunji.common.utils.JsonUtil;
import com.yunji.coffee.entity.dto.CoffeeInfoDTO;
import com.yunji.coffee.entity.dto.ICommonDeviceDetail;
import com.yunji.coffee.entity.dto.WipeDeviceInfoDTO;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;
import java.util.function.Function;

/**
 * @author : peter-zhu
 * @date : 2025/2/8 15:18
 * @description : 自旋获取设备最新状态
 **/
@Slf4j
public class DeviceSpinService {

    private static final int SPIN_TIMES = 20;
    private static final int SPIN_INTERVAL = 500;

    public static <T> void validateCargoStatus(String imei, String typeCode) {
        T status = getStatus(imei, typeCode);
        if (status == null)
            throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);

        if (status instanceof WipeDeviceInfoDTO) {
            WipeDeviceInfoDTO b = (WipeDeviceInfoDTO) status;
            if (b.getInLimitStatus().equals(0) || b.getLockStatus().equals(0))
                throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);
        } else if (status instanceof CoffeeInfoDTO) {
            CoffeeInfoDTO b = (CoffeeInfoDTO) status;
            if (b.getLockStatus().equals(0)) {
                throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);
            }
        }
        log.info("DeviceSpinService validateCargoStatus getStatus={}", JsonUtil.toJsonString(status));
    }

    public static <T> void validateShopStatus(String imei, String typeCode) {
        T status = getStatus(imei, typeCode);
        if (status == null)
            throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);

        if (status instanceof WipeDeviceInfoDTO) {
            WipeDeviceInfoDTO b = (WipeDeviceInfoDTO) status;
            if (b.getInLimitStatus().equals(0) || b.getLockStatus().equals(0))
                throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);
        } else if (status instanceof CoffeeInfoDTO) {
            CoffeeInfoDTO b = (CoffeeInfoDTO) status;
            if (b.getLockStatus().equals(0)) {
                throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);
            }
            for (ICommonDeviceDetail deviceDetail : b.getDetailList()) {
                if (deviceDetail.getMotorStatus().equals(0)){
                    throw new DeviceException(DeviceErrorEnum.CHECKED_202510, deviceDetail.getOrdinal(),imei);
                }
            }
        }
        log.info("DeviceSpinService validateShopStatus getStatus={}", JsonUtil.toJsonString(status));
    }

    public static <T> T getStatus(String imei, String typeCode) {
        return spinGet(imei, a -> {
            if (a == null)
                return false;
            if (a instanceof WipeDeviceInfoDTO) {
                WipeDeviceInfoDTO b = (WipeDeviceInfoDTO) a;
                return b.getInLimitStatus().equals(1) && b.getLockStatus().equals(1);
            } else if (a instanceof CoffeeInfoDTO) {
                CoffeeInfoDTO b = (CoffeeInfoDTO) a;
                return b.getLockStatus().equals(1);
                //  log.info("getStatus CoffeeInfoDTO {}", JsonUtil.toJsonString(b));
//                if (b.getLockStatus().equals(0)) {
//                    throw new DeviceException(DeviceErrorEnum.CHECKED_202507, imei);
//                }
            }
            return true;
        }, ContainerTypeEnum.getClass(typeCode));
    }

    public static <T> T getNewest(String imei, String typeCode) {
        return spinGet(imei, Objects::nonNull, ContainerTypeEnum.getClass(typeCode));
    }

    public static <T> T spinGet(String imei, Function<T, Boolean> compile, Class<T> clazz) {
        DeviceInfoCache.removeInfo(imei);
        boolean ping = DeviceCommandEngine.online(imei);
        if (!ping)
            return null;
        T info;
        for (int i = 0; i < SPIN_TIMES; i++) {
            try {
                Thread.sleep(SPIN_INTERVAL);// 等待 500ms
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
            info = clazz.cast(DeviceInfoCache.getInfo(imei));
            if (info == null) continue;
            Boolean rs = compile.apply(info);
            if (rs)
                return info;
        }
        return null;
    }

}
